package com.mutual.assistance.gateway.config;

import com.alibaba.fastjson.JSON;
import com.mutual.assistance.response.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import static com.mutual.assistance.exception.code.ExceptionCode.UNAUTHORIZED;

/**
 * @author chenyuxin
 * @version 1.0
 * @description 网关全局过滤器
 * @date 2023/4/14 23:48
 */
@Slf4j
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final List<String> whitelist;

    private static final String EXPIRE_ERROR = "认证令牌已过期";
    public static final String SECURITY_WHITELIST = "/security-whitelist.properties";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String CONTENT_TYPE_VALUE_UTF8_JSON = "application/json;charset=UTF-8";
    public static final String BLANK_SPACE = " ";
    public static final String AUTHORIZATION = "Authorization";

    public static final String TOKEN = "mutual.assistance:login:token";

    @Resource
    private TokenStore tokenStore;

    static {
        try (
                InputStream resource = AuthorizeFilter.class.getResourceAsStream(SECURITY_WHITELIST)
        ) {
            Properties properties = new Properties();
            properties.load(resource);
            Set<String> keys = properties.stringPropertyNames();
            whitelist = new ArrayList<>(keys);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl = exchange.getRequest().getPath().value();
        log.info("获取到请求地址：{}", requestUrl);
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String url : whitelist) {
            if (pathMatcher.match(url, requestUrl)) {
                log.info("匹配到：{}", url);
                return chain.filter(exchange);
            }
        }

        String token = getToken(exchange);

        if (StringUtils.isBlank(token)) {
            return buildReturnMono(UNAUTHORIZED.getMsg(), exchange);
        }
        OAuth2AccessToken oAuth2AccessToken;
        oAuth2AccessToken = tokenStore.readAccessToken(token);
        boolean expired = oAuth2AccessToken.isExpired();
        if (expired) {
            return buildReturnMono(EXPIRE_ERROR, exchange);
        }

        return chain.filter(exchange);
    }

    private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        String jsonString = JSON.toJSONString(R.fail(UNAUTHORIZED.getCode(), error));
        byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add(CONTENT_TYPE, CONTENT_TYPE_VALUE_UTF8_JSON);
        return response.writeWith(Mono.just(buffer));
    }

    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst(AUTHORIZATION);
        if (StringUtils.isBlank(tokenStr)) {
            return null;
        }
        String token = tokenStr.split(BLANK_SPACE)[1];
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return token;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
